import io
import os
import shutil
import tempfile



class TemporaryFileWriter(io.BufferedWriter):
    """ファイルをアトミックに更新します。"""

    backup_filepath = None
    __filepath = None
    __temppath = None


    @property
    def filepath(self):
        """ファイルパスを取得します。"""
        return self.__filepath


    @property
    def temppath(self):
        """一時ファイルパスを取得します。"""
        return self.__temppath


    def __init__(self, filepath, mode='wb', buffer_size=io.DEFAULT_BUFFER_SIZE, backup_filepath=None):
        """ファイルパスを指定して初期化します。
        backup_filepath に None 以外が指定された場合、書き込み完了時に
        バックアップファイルが作成されます。
        """
        dirpath, filename = os.path.split(filepath)
        fd, temppath = tempfile.mkstemp(prefix=filename + '.', dir=dirpath)
        try:
            fh = os.fdopen(fd, mode)
            super(TemporaryFileWriter, self).__init__(fh, buffer_size)
        except:
            if fh:
                fh.close()
            os.remove(temppath)
            raise
        self.__filepath = filepath
        self.__temppath = temppath
        self.backup_filepath = backup_filepath


    def __enter__(self):
        return self


    def __exit__(self, exc_type, exc_value, traceback):
        if exc_type is None and exc_value is None and traceback is None:
            self.close()
        else:
            self.abort()


    def close(self):
        """一時ファイルを閉じてリネームします。"""
        if self.closed:
            return
        super(io.BufferedWriter, self).close()
        self.raw.close()
        try:
            if os.path.exists(self.filepath):
                if self.backup_filepath is not None:
                    shutil.move(self.filepath, self.backup_filepath)
                else:
                    os.remove(self.filepath)
            shutil.move(self.temppath, self.filepath)
        except:
            os.remove(self.temppath)
            raise


    def abort(self):
        """一時ファイルを閉じて削除します。"""
        if self.closed:
            return
        super(io.BufferedWriter, self).close()
        self.raw.close()
        os.remove(self.temppath)